package cn.bugstack.springframework.beans.factory.annotation;

import cn.bugstack.springframework.beans.BeansException;
import cn.bugstack.springframework.beans.PropertyValues;
import cn.bugstack.springframework.beans.factory.BeanFactory;
import cn.bugstack.springframework.beans.factory.BeanFactoryAware;
import cn.bugstack.springframework.beans.factory.ConfigurableListableBeanFactory;
import cn.bugstack.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
import cn.bugstack.springframework.util.ClassUtils;
import cn.hutool.core.bean.BeanUtil;

import java.lang.reflect.Field;

/**
 * AutowiredAnnotationBeanPostProcessor 的工作原理是，它在 bean 的创建过程中查找类上和使用 @Autowired、@Value、@Inject 注解
 * 标记的字段和方法。然后，它使用 BeanFactory 来解析这些依赖，并将它们注入到对应的 bean 中。
 *
 * 例如，当一个类有一个使用 @Autowired 注解标记的字段时，AutowiredAnnotationBeanPostProcessor 会在 bean 的生命周期中找到这个字段，
 * 并尝试从 BeanFactory 中找到匹配的 bean 来自动装配这个字段。
 * public class MyService {
 *     @Autowired
 *     private MyDependency myDependency;
 *     // ...
 * }
 * 在这个过程中，AutowiredAnnotationBeanPostProcessor 会处理以下步骤：
 * 在 bean 实例化之后，它会检查 bean 的所有字段和方法，查找是否有使用自动装配注解的成员。
 * 对于每个找到的成员，它会尝试从 BeanFactory 中找到匹配的 bean。
 * 如果找到了匹配的 bean，它会将这些 bean 注入到当前的 bean 中。
 */
public class AutowiredAnnotationBeanPostProcessor implements InstantiationAwareBeanPostProcessor, BeanFactoryAware {

    private ConfigurableListableBeanFactory beanFactory;

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
    }

    /**
     * 在实例化 bean 后处理属性值，主要用于处理 @Value 和 @Autowired 注解
     *
     * @param pvs 属性值，包含待设置的属性信息
     * @param bean 实例化的 Bean 对象
     * @param beanName Bean 的名称
     * @return 修改后的 PropertyValues，此处返回原 pvs 不做修改
     * @throws BeansException 如果处理过程中发生错误
     */
    @Override
    public PropertyValues postProcessPropertyValues(PropertyValues pvs, Object bean, String beanName) throws BeansException {
        // 处理 @Value 注解，为字段设置值
        Class<?> clazz = bean.getClass();
        clazz = ClassUtils.isCglibProxyClass(clazz) ? clazz.getSuperclass() : clazz;

        Field[] declaredFields = clazz.getDeclaredFields();

        // 处理字段上的 @Value 注解，进行值注入
        for (Field field : declaredFields) {
            Value valueAnnotation = field.getAnnotation(Value.class);
            if (null != valueAnnotation) {
                String value = valueAnnotation.value();
                value = beanFactory.resolveEmbeddedValue(value);
                BeanUtil.setFieldValue(bean, field.getName(), value);
            }
        }

        // 处理 @Autowired 注解，为字段注入依赖的 Bean
        for (Field field : declaredFields) {
            Autowired autowiredAnnotation = field.getAnnotation(Autowired.class);
            if (null != autowiredAnnotation) {
                Class<?> fieldType = field.getType();
                String dependentBeanName = null;
                Qualifier qualifierAnnotation = field.getAnnotation(Qualifier.class);
                Object dependentBean = null;
                // 有Qualifier注解时，通过Qualifier值获取Bean
                if (null != qualifierAnnotation) {
                    dependentBeanName = qualifierAnnotation.value();
                    dependentBean = beanFactory.getBean(dependentBeanName, fieldType);
                } else {
                    // 无Qualifier注解时，直接通过类型获取Bean
                    dependentBean = beanFactory.getBean(fieldType);
                }
                BeanUtil.setFieldValue(bean, field.getName(), dependentBean);
            }
        }

        return pvs;
    }

    // 以下方法当前实现均返回null，留待后续扩展

    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        return null;
    }

    @Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        return true;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return null;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return null;
    }
}
